import * as THREE from 'three';
// import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
// import {FlyControls} from 'three/examples/jsm/controls/FlyControls.js';
import {TWEEN} from 'three/examples/jsm/libs/tween.module.min';
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader';
// import {GLTFExporter} from 'three/examples/jsm/exporters/GLTFExporter';
import {DRACOLoader} from 'three/examples/jsm/loaders/DRACOLoader';
import Stats from 'three/examples/jsm/libs/stats.module.js';
import {GUI} from 'three/examples/jsm/libs/dat.gui.module.js';
import {baseUrl} from '../../js/base.js';
import {OrbitControls} from './no_pack_js/OrbitControls';
import {drawPoint, drawLine, drawFace, drawConcaveFace, setModelCenter, cameraMoveLookModel, canvasResize} from './no_pack_js/utils';
import api from './api'

import '../../css/base.css';

let body = document.body,
    canvas = document.getElementById('canvas');
let renderer, scene, camera; // 场景、相机、渲染器

let stats, orbitControls, flyControls, clock = new THREE.Clock(); //

// 建筑物楼层每层数据
let buildingParams = window.buildingParams = {0: {floorNumber: 0, floorH: 6, position: new THREE.Vector3(), object: null, userData: null}};

let buildingGroup = new THREE.Group();
let mouse = new THREE.Vector2(), // 指针在屏幕上的二维位置
    point = new THREE.Vector3(), // 指针在三维上的位置
    mouseOnDownPosition = new THREE.Vector2(); // 指针点击在屏幕上的二维位置
let raycaster = new THREE.Raycaster(); // 射线

let faceGroup = new THREE.Group(),  // 画面的组
    face,
    lineGroup = new THREE.Group(), // 画线的组
    line, // 画玩的线对象
    mousePoint; // 标点是跟随鼠标的点
let pointsList = []; // 画点、线、面临时存放的点数据

// gui 操作参数
const guiParams = window.guiParams = {
    open: true,
    drawPoint: false,
    drawLine: false,
    drawFace: false,
    modelCenter: false,
    setUpFloor: false,
    selectFloorActive: 0, // 选中的楼层
    selectFloor: [0, 1, 2, 3, 4],
}

// 单层楼层点、线、面的数据
let floorParams = window.floorParams = {
    // 单层楼点数据
    points: [{'name': '0', 'floorNumber': 0, 'point': [59, 1, -1]}],
    // 单层楼线数据
    lines: [{'name': '0', 'floorNumber': 0, 'points': [[56, 1, -47], [3, 1, 26], [-66, 1, -25]], 'object': null}],
    // 单层楼面数据
    faces: [{'name': '0', 'floorNumber': 0, 'points': [[48, 1, 25], [43, 1, 5], [9, 1, 29], [-14, 1, 40], [-14, 1, 40]], 'object': null}]
}

// 跨层楼层点、线、面的数据
let floorsParams = window.floorsParams = {
    // 跨层楼点数据
    points: [{name: '', points: [{name: '', floorNumber: 0, point: new THREE.Vector3()}]}],
    // 跨层楼线数据
    lines: [{name: '', points: [{name: '', floorName: 0, point: new THREE.Vector3()}]}],
    // 跨层区域数据
    box: [{name: '', startFloorNumber: 0, endFloorNumber: 1, points: [new THREE.Vector3()]}]
}

// 初始化场景
function initCSR() {
    const w = canvas.offsetWidth;
    const h = canvas.offsetHeight;

    camera = new THREE.PerspectiveCamera(75, w / h, 0.1, 10000);
    // camera = new THREE.OrthographicCamera(-w, w, h, -h, 1, 1000);
    camera.position.set(0, 60, 100);

    window.scene = scene = new THREE.Scene();
    // scene.add(new THREE.AxesHelper(1000))
    // scene.background = new THREE.Color(0xefefef);
    // scene.fog = new THREE.Fog( 0xefefef, 100, 500 );

    renderer = new THREE.WebGLRenderer({canvas, antialias: true, alpha: true, premultipliedAlpha: true});
    renderer.outputEncoding = THREE.sRGBEncoding;
    renderer.sortObjects = false;
    camera.updateProjectionMatrix();
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(w, h, false);
}

// 初始化灯光
function initLight() {
    const ambientLight = new THREE.AmbientLight(0xFFFFFF, .6);
    scene.add(ambientLight);

    const directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1);
    directionalLight.position.set(-10, 10, -10);
    scene.add(directionalLight);
}

// 初始化模型
function initModel() {
    const gltfLoader = new GLTFLoader();
    const dracoLoader = new DRACOLoader();
    dracoLoader.setDecoderPath('/static/examples/js/libs/draco/');
    gltfLoader.setDRACOLoader(dracoLoader);
    let textureLoader = new THREE.TextureLoader();
    const texture = textureLoader.load(baseUrl + 'static/images/floor.jpeg');
    gltfLoader.load(baseUrl + 'static/models/demo-min.glb', onLoad, onProgress, onError);

    function onLoad(gltf) {
        const gltfScene = gltf.scene;
        gltfScene.children.forEach(value => {
            value.traverse(object => {
                object.name = value.name
            })
            buildingParams[value.name] = {floorNumber: value.name, position: new THREE.Vector3(), object: value.clone(), userData: value.userData};
            buildingGroup.add(buildingParams[value.name].object);
            buildingGroup.name = 'buildingGroup';
        });

        // 添加到射线识别的组
        scene.add(buildingGroup);

        setTimeout(() => {
            const object = scene.getObjectByName('buildingGroup')
            scene.add(new THREE.Box3Helper(new THREE.Box3().setFromObject(object), 0xFF0000));


            const box = new THREE.Box3().setFromObject(object.children[0]);
            const boxSize = box.getSize();
            console.log('boxSize', boxSize)
            const center = box.getCenter(new THREE.Vector3());
            console.log('center', center)
        }, 2000)

        if (guiParams.open) setTimeout(() => openOrCloseFloor(true))
    }

    function onProgress(xhr) {
        // console.log(xhr)
    }

    function onError(err) {
        console.error(err)
    }
}

// 初始化控制器
function initControls() {
    {
        orbitControls = new OrbitControls(camera, canvas);
        orbitControls.enableDamping = true;
        orbitControls.screenSpacePanning = true;
        orbitControls.enableKeys = false;
        orbitControls.mouseButtons = {
            LEFT: THREE.MOUSE.PAN,
            MIDDLE: THREE.MOUSE.DOLLY,
            RIGHT: THREE.MOUSE.ROTATE
        };
        window.orbitControls = orbitControls;
    }

    {
        // flyControls = new FlyControls(camera, canvas);
        // flyControls.movementSpeed = 100
        // flyControls.rollSpeed = 1
        // flyControls.dragToLook = true
    }
}

// 初始化帮手
function initHelper() {
    stats = new Stats();
    body.appendChild(stats.domElement);

    const gui = new GUI();

    gui.add(guiParams, 'open').onChange((boolean) => openOrCloseFloor(boolean)).listen()
    gui.add(guiParams, 'modelCenter').onChange(() => {
        // setModelCenter(camera, buildingGroup)
        cameraMoveLookModel(camera, buildingGroup)
    }).listen();

    // 画一个跟随鼠标的点
    mousePoint = drawPoint(scene, new THREE.Vector3());

    gui.add(guiParams, 'drawPoint').onChange((boolean) => {
        if (boolean) mousePoint.material.color = new THREE.Color(0xFF0000);
    }).listen();
    gui.add(guiParams, 'drawLine').onChange((boolean) => {
        if (boolean) {
            pointsList = []; // 清空

            const objectByName = buildingGroup.getObjectByName(guiParams.selectFloorActive);
            // 线信息处理
            line = new THREE.Group();
            line.name = 'line';
            lineGroup = objectByName.getObjectByName('floorLineGroup');
            if (!lineGroup) { // 如果保存这些线的组不存在则创建
                lineGroup = new THREE.Group();
                lineGroup.name = 'floorLineGroup';
                objectByName.add(lineGroup);
            }

            mousePoint.material.color = new THREE.Color(0xFFFF00);
        } else {
            lineGroup = null;
        }
    }).listen();
    gui.add(guiParams, 'drawFace').onChange((boolean) => {
        if (boolean) {
            pointsList = [];

            const objectByName = buildingGroup.getObjectByName(guiParams.selectFloorActive);
            // 面信息处理
            face = new THREE.Group();
            face.name = 'face';
            faceGroup = objectByName.getObjectByName('floorFaceGroup');
            if (!faceGroup) { // 如果保存这些线的组不存在则创建
                faceGroup = new THREE.Group();
                faceGroup.name = 'floorLineGroup';
                objectByName.add(faceGroup);
            }

            mousePoint.material.color = new THREE.Color(0x0000FF);
        } else {
            faceGroup = null;
        }
    }).listen();

    gui.add(guiParams, 'selectFloorActive').options(guiParams.selectFloor).onChange(number => {
        console.log(number);
        guiParams.selectFloorActive = number;

        const objectByName = buildingGroup.getObjectByName(guiParams.selectFloorActive);
        pointsList = [];
        // 线信息处理
        line = new THREE.Group();
        line.name = 'line';
        lineGroup = objectByName.getObjectByName('floorLineGroup');
        if (!lineGroup) { // 如果保存这些线的组不存在则创建
            lineGroup = new THREE.Group();
            lineGroup.name = 'floorLineGroup';
            objectByName.add(lineGroup);
        }
        // 面信息处理
        face = new THREE.Group();
        face.name = 'face';
        faceGroup = objectByName.getObjectByName('floorFaceGroup');
        if (!faceGroup) { // 如果保存这些线的组不存在则创建
            faceGroup = new THREE.Group();
            faceGroup.name = 'floorLineGroup';
            objectByName.add(faceGroup);
        }
    }).listen();
}

// 展开合并楼层
function openOrCloseFloor(boolean) {
    buildingGroup.children.forEach((value, index) => {
        new TWEEN.Tween({position: value.position.y}).to({
            position: boolean ? value.position.y + value.name * 30 : value.position.y - value.name * 30
        }, 500).onUpdate(val => {
            value.position.setY(val.position);
        }).onComplete(value => {
        }).start();
    });
}

// 添加监听器
function addEL() {
    window.addEventListener('resize', () => canvasResize(renderer, camera, canvas.offsetWidth, canvas.offsetHeight))
    canvas.addEventListener('pointerdown', mouseDown, false);
    canvas.addEventListener('pointermove', mouseMove, false);
    canvas.addEventListener('pointerup', mouseUp, false);
    canvas.addEventListener('mouseup', (ev) => {
        console.log(JSON.stringify(pointsList));
        if (ev.button === 2) {

            // 标点 guiParams.drawPoint 是否开启标点
            if (guiParams.drawPoint) {
                const currentlyClickedFloor = buildingGroup.getObjectByName(guiParams.selectFloorActive);
                let points = currentlyClickedFloor.getObjectByName('points');
                if (!points) {
                    points = new THREE.Group();
                    points.name = 'points';
                    currentlyClickedFloor.add(points);
                }
                drawPoint(points, new THREE.Vector3().copy(point.setY(1)));

                floorParams.points.push({
                    name: guiParams.selectFloorActive,
                    floorNumber: +guiParams.selectFloorActive,
                    point: new THREE.Vector3().copy(point.setY(1)),
                });
            }
            if (guiParams.drawLine) {
                floorParams.lines.push({
                    name: guiParams.selectFloorActive,
                    floorNumber: +guiParams.selectFloorActive,
                    points: pointsList,
                    object: null
                });
            }
            if (guiParams.drawFace) {
                floorParams.faces.push({
                    name: guiParams.selectFloorActive,
                    floorNumber: +guiParams.selectFloorActive,
                    points: pointsList,
                    object: null
                });
            }
            mousePoint.visible = false

            guiParams.selectFloorActive = 0
            guiParams.drawPoint = false;
            guiParams.drawLine = false;
            guiParams.drawFace = false;
        }
    })
}

// 按下
function mouseDown(e) {
    //
    mouse.x = (e.offsetX / canvas.offsetWidth) * 2 - 1;
    mouse.y = -(e.offsetY / canvas.offsetHeight) * 2 + 1;
    //
    raycaster.setFromCamera(mouse, camera);
    let intersectObjects = raycaster.intersectObjects(buildingGroup.children, true);
    // 有拾取到东西
    if (intersectObjects.length) {
        const object = intersectObjects[0];
        // 保存鼠标按下的点位置-----为单击操作使用
        mouseOnDownPosition.copy(mouse);

        // 画线的第一个点
        if (guiParams.drawLine || guiParams.drawFace) {
            console.log(pointsList);
            if (guiParams.selectFloorActive === object.object.name) {
                if (pointsList.length < 1) {
                    pointsList = [];
                    pointsList.push([object.point.x, 1, object.point.z]);
                    pointsList.push([object.point.x, 1, object.point.z]);
                } else {
                    pointsList.push([object.point.x, 1, object.point.z]);
                }
            }
        }
    }
}

// 移动
function mouseMove(e) {
    //
    mouse.x = (e.offsetX / canvas.offsetWidth) * 2 - 1;
    mouse.y = -(e.offsetY / canvas.offsetHeight) * 2 + 1;
    //
    raycaster.setFromCamera(mouse, camera);
    let intersectObjects = raycaster.intersectObjects(buildingGroup.children, true);

    // 有拾取到东西
    if (intersectObjects.length) {
        const object = intersectObjects[0];

        // 需要 让点 跟随鼠标
        if (guiParams.drawPoint || guiParams.drawLine || guiParams.drawFace) {
            if (mousePoint) {
                mousePoint.visible = true;
                mousePoint.position.copy(object.point.setY(object.point.y + 1));
            } else {
                mousePoint = drawPoint(scene, new THREE.Vector3());
            }
        }

        // 画线
        if (guiParams.drawLine) {
            pointsList.pop();
            pointsList.push([object.point.x, 1, object.point.z]);
            lineGroup.remove(line);
            line = drawLine(lineGroup, pointsList);
        }

        // 画面
        if (guiParams.drawFace) {
            pointsList.pop();
            pointsList.push([object.point.x, 1, object.point.z]);
            faceGroup.remove(face);
            face = drawConcaveFace(faceGroup, pointsList);
        }

    } else {
        // 隐藏跟随鼠标的点
        if (mousePoint) mousePoint.visible = false;
    }
}

// 弹起
function mouseUp(e) {
    //
    mouse.x = (e.offsetX / canvas.offsetWidth) * 2 - 1;
    mouse.y = -(e.offsetY / canvas.offsetHeight) * 2 + 1;
    //
    raycaster.setFromCamera(mouse, camera);
    let intersectObjects = raycaster.intersectObjects(buildingGroup.children, true);

    // 有拾取到东西
    if (intersectObjects.length) {
        const object = intersectObjects[0];
        console.log(object.object.name)
        // 单击了同一个位置
        if (mouseOnDownPosition.equals(mouse)) {
            // 设置焦点
            guiParams.selectFloorActive = object.object.name; // active状态的楼层
            point.copy(object.point)
        }
    }
}

// 渲染
function render() {
    if (orbitControls) orbitControls.update();
    if (flyControls) flyControls.update(clock.getDelta());
    if (TWEEN) TWEEN.update();
    if (stats) stats.update();

    renderer.render(scene, camera);
}

// 循环渲染
function animation() {
    requestAnimationFrame(animation);
    render();
}

// 启动
(function () {
    initCSR();
    initLight();
    initHelper();
    initModel();
    initControls();
    addEL();
    // setTimeout(() => render(), 100)
    animation();
})();

// 获取并初始化-点数据
function getAndSetPoints() {
    floorParams.points.forEach(value => {
        const buildingParam = buildingParams[value.name];
        if (buildingParam.object.getObjectByName('points')) {
            const points = new THREE.Group();
            points.name = 'points';
        }
        drawPoint(buildingParam.object, new THREE.Vector3(...value.point));
    });
}

// 获取并初始化-线数据
function getAndSetLine() {
    floorParams.lines.forEach(value => {
        const buildingParam = buildingParams[value.name];
        if (buildingParam.object.getObjectByName('floorLineGroup')) {
            const floorLineGroup = new THREE.Group();
            floorLineGroup.name = 'floorLineGroup';
        }
        drawLine(buildingParam.object, value.points);
    });
}

// 获取并初始化-面数据
function getAndSetFace() {
    floorParams.faces.forEach(value => {
        const buildingParam = buildingParams[value.name];
        if (buildingParam.object.getObjectByName('floorFaceGroup')) {
            const floorLineGroup = new THREE.Group();
            floorLineGroup.name = 'floorFaceGroup';
        }
        drawLine(buildingParam.object, value.points, {isLine: true});
        drawConcaveFace(buildingParam.object, value.points);
    });
}

const interval = setInterval(async () => {
    if (buildingGroup.getObjectByName('2')) {
        const queryPoint = await api.queryPoint()
        const queryLine = await api.queryLine()
        const queryFace = await api.queryFace()

        queryPoint.map(value => value.name = value.floorNumber)
        floorParams.points = queryPoint
        queryLine.map(value => value.name = value.floorNumber)
        floorParams.lines = queryLine
        queryFace.map(value => value.name = value.floorNumber)
        floorParams.faces = queryFace

        getAndSetPoints();
        getAndSetLine();
        getAndSetFace();
        clearInterval(interval);
    }
}, 200);


